package cn.clj1.builtInFunctions;

import cn.clj1.cell.CljCell;
import cn.clj1.cell.CljCellType;
import cn.clj1.func.CljFunc;
import cn.clj1.func.CljFuncParam;
import cn.clj1.func.CljFuncParamList;
import cn.clj1.func.MyCljFunc;
import cn.clj1.recur.CljRecur;
import cn.clj1.MyClojureInterp;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

/**
 * 函数的定义
 * */
public class CljFunctions {

    private static boolean hasRest(List<CljFuncParam> lst) {
        for (CljFuncParam m : lst)
        {
            if (m.getRestParam() != null)
            {
                return true;
            }
        }
        return false;
    }

    /**
     * function 中 funcOverride 添加 方法
     * */
    private static void addToFunc(MyCljFunc myCljFunc, CljFunc func) throws Exception {
        if (myCljFunc.getFuncOverride().containsKey(func.getFuncParams().size())) {
            throw new Exception("unexpected 相同的参数数量不能重载！");
        } else {
            for (Integer key : myCljFunc.getFuncOverride().keySet()) {
                CljFunc func1 = myCljFunc.getFuncOverride().get(key);
                if (hasRest(func1.getFuncParams()) && func1.getFuncParams().size() <= func.getFuncParams().size()) {
                    throw new Exception("unexpected 重载中的可变参数函数，中的参数数量不可以小于或等于其它函数的参数数量！");
                } else if (hasRest(func1.getFuncParams()) && hasRest(func.getFuncParams())) {
                    throw new Exception("unexpected 重载函数不可以有多个可变参数的函数");
                }
            }
            myCljFunc.getFuncOverride().put(func.getFuncParams().size(), func);
        }
    }

    private static List<CljFuncParamList> toFuncParamList(List<CljCell> lst) throws Exception {
        List<CljFuncParamList> rs = new ArrayList<>();
        for (int i = 0; i < lst.size(); i++)
        {
            CljCell c = lst.get(i);
            if (c.getCljCellType() == CljCellType.symbol || c.getCljCellType() == CljCellType.percent) {
                rs.add(new CljFuncParamList(c.getCell().toString()));
            } else if (c.getCljCellType() == CljCellType.and_clj) {
                if (i == lst.size() - 2) {
                    CljFuncParamList cljFuncParam = new CljFuncParamList();
                    cljFuncParam.setName(lst.get(1 + i).getCell().toString());
                    rs.add(cljFuncParam);
                    break;
                }
            } else {
                throw new Exception("函数参数错误：" + lst.get(i).toString());
            }
        }
        if (rs.size() == 2) {
            return rs;
        }
        throw new Exception("函数参数错误：在函数参数中解析 [] 的格式通常是 [frist & rest]");
    }

    /**
     * 解析函数几种输入参数
     * 1、[a b c]
     * 2、[a b & c]
     * 3、[a b [c & d]]
     * */
    private static List<CljFuncParam> toFuncParam(List<CljCell> lst) throws Exception {
        List<CljFuncParam> rs = new ArrayList<>();
        for (int i = 0; i < lst.size(); i++)
        {
            CljCell c = lst.get(i);
            if (c.getCljCellType() == CljCellType.symbol || c.getCljCellType() == CljCellType.percent) {
                rs.add(new CljFuncParam(c.getCell().toString()));
            } else if (c.getCljCellType() == CljCellType.and_clj) {
                if (i == lst.size() - 2) {
                    CljFuncParam cljFuncParam = new CljFuncParam();
                    cljFuncParam.setRestParam(lst.get(1 + i).getCell().toString());
                    rs.add(cljFuncParam);
                    break;
                }
            } else if (c.getCljCellType() == CljCellType.bracket) {
                List<CljFuncParamList> cljFuncParamLists = toFuncParamList((ArrayList<CljCell>) c.getCell());
                CljFuncParam cljFuncParam = new CljFuncParam();
                cljFuncParam.setCljFuncParamList(cljFuncParamLists);
                rs.add(cljFuncParam);
            }
            else {
                throw new Exception("函数参数错误：" + lst.get(i).toString());
            }
        }
        return rs;
    }

    private static CljFunc to_param_body(String fname, List<CljCell> param_body) throws Exception {
        CljCell fb = param_body.get(0);
        // 如果是 [a b] 这种类型
        if (fb.getCljCellType() == CljCellType.bracket) {
            // 函数参数
            List<CljFuncParam> funcParams = toFuncParam((List<CljCell>) fb.getCell());

            List<CljCell> lst_body = param_body.subList(1, param_body.size());
            if (lst_body != null && lst_body.size() > 0)
            {
                // 将函数存储到 funcs 列表
                //this.funcs.put(fname, new MyCljFunc(new CljFunc(funcParams, lst_body.get(0))));
                return new CljFunc(funcParams, lst_body.get(0));
            }
        }
        throw new Exception("unexpected " + fname + " 输入参数的格式错误！");
    }

    private static CljFunc to_param_body(List<CljCell> param_body) throws Exception {
        CljCell fb = param_body.get(0);
        // 如果是 [a b] 这种类型
        if (fb.getCljCellType() == CljCellType.bracket) {
            // 函数参数
            List<CljFuncParam> funcParams = toFuncParam((List<CljCell>) fb.getCell());

            List<CljCell> lst_body = param_body.subList(1, param_body.size());
            if (lst_body != null && lst_body.size() > 0)
            {
                // 将函数存储到 funcs 列表
                //this.funcs.put(fname, new MyCljFunc(new CljFunc(funcParams, lst_body.get(0))));
                return new CljFunc(funcParams, lst_body.get(0));
            }
        }
        throw new Exception("unexpected 匿名函数，输入参数的格式错误！");
    }

    /**
     * 执行 (defn) 里面的内容
     * */
    public static void Clj_list_defn(List<CljCell> lst, Hashtable<String, CljCell> funcHs, CljRecur cljRecur, CljCell rs) throws Exception {
        CljCell f = lst.get(1);

        if (f != null) {
            // 函数名
            String fname = f.getCell().toString();

            List<CljCell> cellList = lst.subList(2, lst.size());

            if (cellList.size() == 2 && cellList.get(0).getCljCellType() == CljCellType.bracket && cellList.get(1).getCljCellType() == CljCellType.Clj_list)
            {
                CljFunc func = to_param_body(fname, cellList);
                MyClojureInterp.funcs.put(fname, new MyCljFunc(func));
            }
            else
            {
                MyCljFunc myCljFunc = new MyCljFunc(true);
                for (CljCell c1: cellList)
                {
                    if (c1.getCljCellType() == CljCellType.Clj_list)
                    {
                        CljFunc func = to_param_body(fname, (List<CljCell>) c1.getCell());
                        addToFunc(myCljFunc, func);
                        //myCljFunc.getFuncOverride().put(func.getFuncParams().size(), func);
                    }
                }
                MyClojureInterp.funcs.put(fname, myCljFunc);
            }
        }
    }

    /**
     * 匿名函数
     * lst :  size = 3  (fn [a b] (+ a b))
     * */
    public static MyCljFunc Clj_list_fn(List<CljCell> lst, Hashtable<String, CljCell> funcHs, CljRecur cljRecur, CljCell rs) throws Exception {
        List<CljCell> cellList = lst.subList(1, lst.size());
        if (cellList.size() == 2 && cellList.get(0).getCljCellType() == CljCellType.bracket && cellList.get(1).getCljCellType() == CljCellType.Clj_list)
        {
            CljFunc func = to_param_body(cellList);
            return new MyCljFunc(func);
        }
        else
        {
            MyCljFunc myCljFunc = new MyCljFunc(true);
            for (CljCell c1: cellList)
            {
                if (c1.getCljCellType() == CljCellType.Clj_list)
                {
                    CljFunc func = to_param_body((List<CljCell>) c1.getCell());
                    addToFunc(myCljFunc, func);
                    //myCljFunc.getFuncOverride().put(func.getFuncParams().size(), func);
                }
            }
            return myCljFunc;
        }
    }
}
